#pragma once

#include <QThread>
#include <cstdio>
#include <io.h>
#include <fcntl.h>

#include <iostream>

#include "model_console.h"

namespace models {

class StdOutThread : public QThread
{
public:
  StdOutThread(Console * pConsole, int file);

  void run();

private:

  Console * mpConsole;
  int mOldFile;
  int mPipe[2];
};

StdOutThread::StdOutThread(Console * pConsole, int file)
{
  mpConsole = pConsole;

  mOldFile = _dup(file);
  _pipe(mPipe, 256, _O_BINARY);
  _dup2(mPipe[1], file);
}

void StdOutThread::run()
{
  char buf[256];

  while (true)
  {
    int numRead = _read(mPipe[0], buf, sizeof(buf));
    mpConsole->SendOutput(std::string(buf, buf + numRead).c_str());
    write(mOldFile, buf, numRead);
  }
}

class StdInThread : public QThread
{
public:
  StdInThread(Console * pConsole);

  void run();

private:

  Console * mpConsole;
};

StdInThread::StdInThread(Console * pConsole)
{
  mpConsole = pConsole;
}

void StdInThread::run()
{
  std::string line;

  while (true)
  {
    std::getline(std::cin, line);
    mpConsole->SendInput(QString(line.c_str()));
  }
}

Console::Console(QObject * Parent_)
: QObject(Parent_)
{
}

Console::~Console()
{
}

void Console::SendInput(const QString & Text_)
{
  emit OnInput(Text_);
}

void Console::SendOutput(const QString & Text_)
{
  emit OnOutput(Text_);
}

void Console::AttachStdIn()
{
  StdInThread *pIO = new StdInThread(this);
  pIO->start();
}

void Console::AttachStdOut()
{
  FILE * pFile = stdout;

  setvbuf(stdout, NULL, _IONBF, 0);

  StdOutThread *pIO = new StdOutThread(this, pFile->_file);
  pIO->start();
}

} // namespace models